package top.bug.blog.utils;

import com.zaxxer.hikari.HikariDataSource;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.software.os.OperatingSystem;
import oshi.util.Util;
import top.bug.blog.entity.dto.sys.ServerInfoDTO;

import javax.sql.DataSource;
import java.io.File;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.net.InetAddress;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author <a href="https://github.com/jiusi66">九思.</a>
 * @email 10837907@qq.com
 * @from <a href="https://www.52bug.top">递归茶馆-www.52bug.top</a>
 * @description 服务信息采集工具类
 */
public class ServerInfoCollectorUtil {

    private static final SystemInfo OSHI = new SystemInfo();

    public static ServerInfoDTO collect(DataSource dataSource) {
        ServerInfoDTO dto = new ServerInfoDTO();
        dto.setSystem(getSystemInfo());
        dto.setCpu(getCpuInfo());
        dto.setMemory(getMemoryInfo());
        dto.setJvm(getJvmInfo());
        dto.setDisks(getDiskInfo());
        dto.setDb(getDbInfo(dataSource));
        return dto;
    }

    public static ServerInfoDTO.SystemInfo getSystemInfo() {
        OperatingSystem os = OSHI.getOperatingSystem();
        ServerInfoDTO.SystemInfo dto = new ServerInfoDTO.SystemInfo();
        try {
            InetAddress localHost = InetAddress.getLocalHost();
            dto.setHostName(localHost.getHostName());
        } catch (Exception e) {
            dto.setHostName("Unknown");
        }
        dto.setOsName(os.toString());
        dto.setOsArch(System.getProperty("os.arch"));
        dto.setOsVersion(System.getProperty("os.version"));
        return dto;
    }

    public static ServerInfoDTO.CpuInfo getCpuInfo() {
        CentralProcessor processor = OSHI.getHardware().getProcessor();
        long[] prevTicks = processor.getSystemCpuLoadTicks();
        // 0.5秒采样
        Util.sleep(500);
        long[] ticks = processor.getSystemCpuLoadTicks();

        long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
        long system = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
        long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
        long total = user + system + idle;

        ServerInfoDTO.CpuInfo info = new ServerInfoDTO.CpuInfo();
        info.setCores(processor.getLogicalProcessorCount());
        info.setUserUsage((double) user / total * 100);
        info.setSystemUsage((double) system / total * 100);
        info.setIdle((double) idle / total * 100);
        return info;
    }

    public static ServerInfoDTO.MemoryInfo getMemoryInfo() {
        GlobalMemory memory = OSHI.getHardware().getMemory();
        ServerInfoDTO.MemoryInfo info = new ServerInfoDTO.MemoryInfo();
        info.setTotal(memory.getTotal());
        info.setFree(memory.getAvailable());
        info.setUsed(memory.getTotal() - memory.getAvailable());
        return info;
    }

    public static ServerInfoDTO.JvmInfo getJvmInfo() {
        Runtime runtime = Runtime.getRuntime();
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean();
        List<GarbageCollectorMXBean> gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans();

        long gcCount = 0;
        long gcTime = 0;
        for (GarbageCollectorMXBean gcMxBean : gcMxBeans) {
            long count = gcMxBean.getCollectionCount();
            long time = gcMxBean.getCollectionTime();
            if (count != -1) {
                gcCount += count;
            }
            if (time != -1) {
                gcTime += time;
            }
        }
        ServerInfoDTO.JvmInfo info = new ServerInfoDTO.JvmInfo();
        info.setMaxMemory(runtime.maxMemory());
        info.setTotalMemory(runtime.totalMemory());
        info.setFreeMemory(runtime.freeMemory());
        info.setUsedMemory(runtime.totalMemory() - runtime.freeMemory());
        info.setUptime(ManagementFactory.getRuntimeMXBean().getUptime());
        info.setThreadCount(threadMXBean.getThreadCount());
        info.setPeakThreadCount(threadMXBean.getPeakThreadCount());
        info.setLoadedClassCount(classLoadingMXBean.getTotalLoadedClassCount());
        info.setGcCount(gcCount);
        info.setGcTime(gcTime);
        return info;
    }

    public static List<ServerInfoDTO.DiskInfo> getDiskInfo() {
        File[] roots = File.listRoots();
        List<ServerInfoDTO.DiskInfo> list = new ArrayList<>();
        for (File root : roots) {
            ServerInfoDTO.DiskInfo info = new ServerInfoDTO.DiskInfo();
            info.setMount(root.getAbsolutePath());
            info.setType(root.getPath());
            info.setTotal(root.getTotalSpace());
            info.setFree(root.getFreeSpace());
            info.setUsed(root.getTotalSpace() - root.getFreeSpace());
            list.add(info);
        }
        return list;
    }

    public static ServerInfoDTO.DbInfo getDbInfo(DataSource dataSource) {
        ServerInfoDTO.DbInfo info = new ServerInfoDTO.DbInfo();
        try (Connection conn = dataSource.getConnection()) {
            DatabaseMetaData meta = conn.getMetaData();
            info.setDriverName(meta.getDriverName());
            info.setDriverVersion(meta.getDriverVersion());
        } catch (SQLException e) {
            info.setDriverName("N/A");
            info.setDriverVersion("N/A");
        }

        if (dataSource instanceof HikariDataSource) {
            HikariDataSource hikari = (HikariDataSource) dataSource;
            info.setActiveConnections(hikari.getHikariPoolMXBean().getActiveConnections());
            info.setMaxConnections(hikari.getMaximumPoolSize());
            info.setMinConnections(hikari.getMinimumIdle());
        }
        return info;
    }
}
